package pa2;

import java.rmi.RemoteException;
import java.util.HashMap;

import pa2.node.Node;
import pa2.node.NodeData;
import pa2.system.Peer;
import pa2.system.SystemManagerImpl;
import pa2.util.CompInteger;


public class CustomSystemManager extends SystemManagerImpl {

	/**
	 * 
	 */
	private static final long serialVersionUID = -348850750957851122L;
//	public static final int RANGE = 65535;
	public static final int RANGE = 1000;
	public Node<CompInteger, String, HashMap<CompInteger, String>> rootNode;
	
	public CustomSystemManager() throws RemoteException {
		super();
	}

	protected synchronized void peersChanged() throws RemoteException {
		//TODO remove peer if remote object invocation fails
		if (peers.size() == 4) {
			System.out.println("***STARTING TO BUILD THE TREE***");
			rootNode = this.createTree(0, 0, 5, RANGE);			
			System.out.println("***FINISHED BUILDING THE TREE***");
		}
	}
	
	
	private Node<CompInteger,String,HashMap<CompInteger,String>> createNode (
			CompInteger comp, NodeData<CompInteger, String, HashMap<CompInteger, String>> data, 
			int siblingIndex, int currentDepth,int maxDepth) throws RemoteException{
		if (currentDepth != maxDepth) {
			//FIXME 1 peer gets 2 rows, rest gets 1 row
			Peer peer = this.peers.get(currentDepth % this.peers.size() );
			/*
			int offset = 0;
			if (currentDepth>=1) {
				offset = (int) Math.pow(2, currentDepth-1);
			}
			Peer peer = this.peers.get((siblingIndex+offset) % this.peers.size());
			*/
//			System.out.println("["+peer.getName()+"]: BRANCH<"+comp.toString()+">");
			return peer.createNode(comp, data);
		} else {
			Peer peer = this.peers.get(siblingIndex % this.peers.size());
//			System.out.println("["+peer.getName()+"]: LEAF<"+comp.toString()+">");
			return peer.createNode(comp, data);
		}
	}
	
	private Node<CompInteger, String, HashMap<CompInteger, String>>
		createTree(int currentSiblingIndex, int currentDepth, int maxDepth, long range) 
			throws RemoteException {
		int denominator =  (int) Math.pow(2, currentDepth+1);
		CompInteger comp =  new CompInteger((int) ((2*currentSiblingIndex+1)*range/denominator));
		Node<CompInteger, String, HashMap<CompInteger, String>> node;
		
		if (currentDepth != maxDepth) {
			node = this.createNode(comp, null, currentSiblingIndex, currentDepth, maxDepth);
			node.addChild(createTree(2*currentSiblingIndex, currentDepth+1, maxDepth, range));
			
			node.addChild(createTree(2*currentSiblingIndex+1, currentDepth+1, maxDepth, range));
		} else {
			HashMap<CompInteger, String> map = new HashMap<CompInteger,String>();
			
			NodeData<CompInteger, String, HashMap<CompInteger, String>> data = 
				new NodeData<CompInteger, String, HashMap<CompInteger,String>>(map);
			
			node = this.createNode(comp, data, currentSiblingIndex, currentDepth, maxDepth);
		}
		
		return node;
	}
}
